package com.ztspeech;

import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;

import com.ztspeech.ztcodec.ZtCodec2;

import org.dragonnova.meetingserver.utils.LogUtil;

import java.io.IOException;
import java.io.InputStream;

/**
 * 紫东语音合成接口
 * Created by Administrator on 2015/6/26.
 * Author: Songxy
 * Email: thinkdata@163.com
 */
public class ZTSynthesizer extends AZTClient {

    private final static String TAG = ZTSynthesizer.class.getName();
    /**
     *
     */
    private Thread synthesizerThread;
    /**
     * 编码
     */
    private ZtCodec2 ztCodec = new ZtCodec2();
    /**
     * 要识别的文字
     */
    private String synText = "";
    /**
     * 解码结果
     */
    private byte[] synResultWave;
    /**
     *
     */
    private final int MAX_SYN_TIME = 60 * 10; // 10 minutes
    /**
     *
     */
    private final int FRAME_LENGTH = 320 * 2;
    /**
     *
     */
    private long decoder;
    /**
     *
     */
    private static final String serviceType = "t2s";
    /**
     *
     */
    private Thread currentThread;

    Handler mHandler;

    public void setSpeed(float speed) {
        if ((double) speed < 0.5D) {
            speed = 0.5F;
        } else if (speed > 2.0F) {
            speed = 2.0F;
        }
        this.speed = speed;
    }

    public ZTSynthesizer(Handler handler) {
        this.mHandler = handler;
        this.setGender(GENDER_MALE);
        this.setSpeed(1.0F);
        this.setLanguage(LANGUAGE_ENGLISH);
    }

    /**
     * 识别线程
     */
    private Runnable synthesizer = new Runnable() {
        @Override
        public void run() {
            Thread thisThread = Thread.currentThread();
            synchronized (this) {
                if (thisThread == ZTSynthesizer.this.currentThread) {
                    //init
                    try {
                        LogUtil.e(TAG, "synthesizing........");
                        byte[] synBytes = (byte[]) doPost(synText);
                        if (synBytes != null && synBytes.length > 0) {
                            synResultWave = decode(synBytes);
                            //提交
                            try {
                                Message msg = mHandler.obtainMessage(AppAudio.SYNTHESIZED);
                                msg.obj = synResultWave;
                                if (thisThread == ZTSynthesizer.this.currentThread) {
                                    mHandler.sendMessage(msg);
                                }
                                LogUtil.e(TAG, "synthesized........");
                            } catch (Exception e) {
                                LogUtil.e(TAG, e.getMessage());
                            }
                        } else {
                            LogUtil.e(TAG, "no response when synthesizing........");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        Message msg = mHandler.obtainMessage(AppAudio.SYNTHESIZE_ERROR);
                        mHandler.sendMessage(msg);
                    } finally {
                        releaseDecoder();
                    }
                }
            }
        }
    };

    private byte[] decode(byte[] data) {
        decoder = ztCodec.createDecoder();
        byte[] frameData = new byte[256];
        short[] frameSamples = new short[FRAME_LENGTH / 2];
        int pos = 0, frameNum = 0;
        while (pos < data.length) {
            int num = data[pos++];
            if (num <= 0 || pos + num > data.length)
                break;
            frameNum++;
            pos += num;
        }
        if (frameNum <= 0) return null;

        byte[] samples = new byte[frameNum * FRAME_LENGTH];
        pos = 0;
        int frameIdx = 0;
        while (pos < data.length) {
            int num = data[pos++];
            if (num <= 0 || pos + num > data.length)
                break;
            System.arraycopy(data, pos, frameData, 0, num);
            int sampleNum = ztCodec.decode(decoder, frameData, num, frameSamples);
            if (sampleNum != FRAME_LENGTH / 2)
                LogUtil.d("decode", "decode frame Error position at: " + sampleNum);
            System.arraycopy(shortArray2ByteArray(frameSamples, 0, FRAME_LENGTH / 2), 0, samples,
                    frameIdx * FRAME_LENGTH, FRAME_LENGTH);
            frameIdx++;
            pos += num;
        }
        return samples;
    }

    private byte[] shortArray2ByteArray(short[] shortData, int offset, int count) {
        byte[] byteData = new byte[count * 2];
        int bytePos = 0;
        while (offset < shortData.length && count > 0) {
            byteData[bytePos] = (byte) (shortData[offset] & 0xff);
            byteData[bytePos + 1] = (byte) (shortData[offset] >>> 8);
            offset++;
            count--;
            bytePos += 2;
        }
        return byteData;
    }

    protected String formatParam() {
        if (this.language == null)
            this.language = LANGUAGE_ENGLISH;
        return String.format("&sc=opu&t=%s&i=%s&g=%s&spd=%s", serviceType, language, gender, speed);
    }

    protected byte[] doResponse(@NonNull InputStream input) throws Exception {
        //
        String key;
        int contentLength = 0;
//        if (conn == null || (input.available() < 1)) return null;
        for (int i = 1; (key = conn.getHeaderFieldKey(i)) != null; i++) {
            if (key.equalsIgnoreCase("Content-Length")) {
                contentLength = Integer.valueOf(conn.getHeaderField(key));
                if (contentLength < 0)
                    contentLength = 0;
                else if (contentLength > 3500 * MAX_SYN_TIME)
                    contentLength = 3500 * MAX_SYN_TIME;
            }
        }
        if (contentLength == 0)
            contentLength = 3500 * MAX_SYN_TIME;

        byte[] synBytes = null;
        try {
            synBytes = new byte[contentLength];
            int readBytes = 0, r;
            while ((r = input.read()) != -1) {
                synBytes[readBytes] = (byte) r;
                readBytes++;
            }
            if (readBytes != synBytes.length) {
                if (readBytes == 0)
                    synBytes = null;
                else {
                    byte[] tmpBytes = new byte[readBytes];
                    System.arraycopy(synBytes, 0, tmpBytes, 0,
                            readBytes);
                    synBytes = tmpBytes;
                }
            }
        } catch (IOException e) {
            LogUtil.e(TAG, "Read InputStream Error: " + e.getMessage());
        } finally {
            if (input != null) input.close();
            releaseDecoder();
        }
        return synBytes;
    }

    protected void releaseDecoder() {
        if (decoder != 0) {
            ztCodec.destroyDecoder(decoder);
            decoder = 0;
        }
    }

    @Override
    protected void finalize() throws Throwable {
        releaseDecoder();
        super.finalize();
    }

    public boolean start(String synText) {
        this.synText = synText;
        synResultWave = null;
        synthesizerThread = new Thread(synthesizer);
        currentThread = synthesizerThread;
        synthesizerThread.start();
        return true;
    }

    /**
     * 停止识别
     */
    public void stopSynthesize(){
        currentThread = null;
    }

}
